package com.hanxiaozhang.zookeeper;

import com.hanxiaozhang.AbstractDistributedLock;
import com.hanxiaozhang.Configuration;
import com.hanxiaozhang.exception.DistributedLockException;
import com.hanxiaozhang.util.DistributedLockUtil;
import com.hanxiaozhang.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;

import java.util.concurrent.TimeUnit;

/**
 * 功能描述: <br>
 * 〈zk分布式锁〉
 *
 * @Author:hanxinghua
 * @Date: 2022/8/29
 */
@Slf4j
public class ZkDistributedLock extends AbstractDistributedLock {

    private CuratorFramework client;

    private InterProcessLock lock;

    private String baseLockPath;

    /**
     * 构造函数
     *
     * @param baseLockPath 基础路径
     * @param lockKey      锁key
     * @param client       zk客户端
     */
    public ZkDistributedLock(String baseLockPath, String lockKey, CuratorFramework client) {
        super(lockKey);
        this.baseLockPath = baseLockPath;
        this.client = client;
        this.lock = getInterProcessMutex(client, getZkLockPath(baseLockPath, lockKey));
    }

    /**
     * 构造函数
     *
     * @param baseLockPath 基础路径
     * @param lockKey      锁key
     * @param client       客户端
     * @param config       配置文件
     */
    public ZkDistributedLock(String baseLockPath, String lockKey, CuratorFramework client, Configuration config) {
        super(lockKey);
        this.client = client;
        this.lock = getInterProcessMutex(client, getZkLockPath(baseLockPath, lockKey));
    }

    /**
     * 构造函数
     *
     * @param lockKey 锁key
     * @param client  客户端
     * @param config  配置文件
     */
    public ZkDistributedLock(String lockKey, CuratorFramework client, Configuration config) {
        super(lockKey);
        this.client = client;
        this.lock = getInterProcessMutex(client, config.getZkLockPath(lockKey));
    }

    @Override
    public String getLockKey() {
        if (StringUtil.isNotBlank(this.baseLockPath)) {
            return getZkLockPath(this.baseLockPath, super.lockKey);
        }
        return super.lockKey;
    }


    @Override
    public void lock() throws DistributedLockException {
        try {
            lock.acquire();
        } catch (Exception e) {
            throw new DistributedLockException("lockKey是 " + lockKey + " 加锁异常: ", e);
        }
    }

    @Override
    public Boolean tryLock() throws DistributedLockException {
        try {
            return lock.acquire(0, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new DistributedLockException("lockKey是 " + lockKey + "尝试加锁异常: ", e);
        }
    }

    @Override
    public Boolean tryLock(long time, TimeUnit timeUnit) throws DistributedLockException {
        try {
            return lock.acquire(time, timeUnit);
        } catch (Exception e) {
            throw new DistributedLockException("lockKey是 " + lockKey + "尝试加锁异常: ", e);
        }
    }

    @Override
    public void unlock() throws DistributedLockException {
        try {
            lock.release();
        } catch (Exception e) {
            throw new DistributedLockException("lockKey是 " + lockKey + "释放锁异常: ", e);
        }
    }

    /**
     * 获取客户端
     *
     * @return
     */
    public CuratorFramework getClient() {
        return client;
    }


    /**
     * 获取锁
     *
     * @param client
     * @param lockPath
     * @return
     */
    private InterProcessLock getInterProcessMutex(CuratorFramework client, String lockPath) {
        return new InterProcessMutex(client, lockPath);
    }


    /**
     * 获取加锁路径
     *
     * @param baseLockPath
     * @param lockKey
     * @return
     */
    private String getZkLockPath(String baseLockPath, String lockKey) {
        String path = baseLockPath;
        if (path.endsWith(DistributedLockUtil.SEPARATOR)) {
            path += lockKey;
        } else {
            path += DistributedLockUtil.SEPARATOR + lockKey;
        }
        return path;
    }

}
